package com.qf.drbackend.conf;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.qf.drbackend.filters.LoginFilter;
import com.qf.drbackend.filters.VerityTokenFilter;
import com.qf.drbackend.pojos.R;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    //因为我们数据库的密码是没有加密的，所以我们要返回一个不加密的密码加密器
    @Bean
    public PasswordEncoder passwordEncoder(){
//        return NoOpPasswordEncoder.getInstance();
        return new BCryptPasswordEncoder();
    }

    //做security相关配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //放开跨站访问攻击支持(默认不放开，只支持自己登录页面的访问)
        http.csrf().disable();
        //放开对frame帧标签的支持
        http.headers().frameOptions().disable();

        //设定跨域支持
        http.cors().configurationSource(cs());

        //添加登录过滤器和token认证过滤器
        http.addFilter(new LoginFilter(authenticationManager()))
                .addFilter(new VerityTokenFilter(authenticationManager()));

        http.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
            @Override
            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                //访问资源时，如果是未登录状态，需要告诉页面我们自定义的数据，而不是提示403状态码
                //提示重新登录
                R r = R.error("NOTLOGIN");
                String json = new ObjectMapper().writeValueAsString(r);
                response.getWriter().write(json);
            }
        });

        //设定登录认证的规则
        //所有资源都不需要登录认证，也不需要权限控制
//        http.authorizeRequests().antMatchers("/**").permitAll();

        //authenticated 要求所有资源访问必须登录
        http.authorizeRequests()
                //将图片上传下载、支付结果通知相关的请求放行，不需要登录验证
                .antMatchers("/common/**","/pay/**").permitAll()
                .antMatchers("/**").authenticated();

        //设定访问权限的规则

        //关闭session的支持(security默认保存用户的信息方式是在session中)
        http.sessionManagement().disable();
    }

    /**
     * 配置跨域访的配置源信息
     * @return
     */
    private CorsConfigurationSource cs() {
        UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource();

        //配置哪些资源需要支持跨域
        CorsConfiguration corsConfig = new CorsConfiguration();

        //支持所有的来源访问
        corsConfig.setAllowedOrigins(Arrays.asList("*"));
        //支持所有请求方式的跨域访问
        corsConfig.setAllowedMethods(Arrays.asList("*"));
        //允许跨域访问时携带任意头信息（不包含认证相关的，比如cookie）
        corsConfig.setAllowedHeaders(Arrays.asList("*"));

        //支持跨域访问时，携带认证信息，比如cookie(我们不使用session保存用户信息，所以我可以不要cookie)
//        corsConfig.setAllowCredentials(true);

        //跨域访问时，支持任意头信息的返回
        corsConfig.setExposedHeaders(Arrays.asList("*"));

        configurationSource.registerCorsConfiguration("/**",corsConfig);

        return configurationSource;
    }

    public static void main(String[] args) {
        String hashpw = BCrypt.hashpw("123", BCrypt.gensalt());
        System.out.println(hashpw);
    }
}
